package com.ivan.toolkit.network;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;
import java.net.SocketTimeoutException;

import lombok.extern.log4j.Log4j2;

@Log4j2
public abstract class UdpNetwork implements Runnable {
	
	DatagramSocket socket = null;
	
	boolean stop = false;
	
	Thread thread = null;
	
	
	public void start() {
		
		if(thread == null) {
			thread = new Thread(this);
			thread.setName("UDP PROVIDER");
			thread.setDaemon(true);
			thread.start();
		}
		
	}
	
	public void stop() {
		
		stop = true;
		
		try {
			if(thread != null) {
				thread.join(3000);
				thread = null;
			}
		} catch (InterruptedException e) {
		}
		
	}
	
	public boolean started() {
		return thread != null && thread.isAlive();
	}
	
	public boolean ready() {
		return socket != null && !socket.isClosed();
	}
	
	@Override
	public void run() {
		
		stop = false;
		
		while(!stop) {
			
			// create udp socket
			{
				
				int localPort = getListenPort();
				
				try {
					
					socket = new DatagramSocket(localPort);
					
					socket.setSoTimeout(2000);
					
					log.info("");
					
				} catch (SocketException e) {
					
					log.error("Create UDP socket failed, port is " + localPort, e);
					
				}
				
				if(socket == null) { 
					try {
						Thread.sleep(5000);
					} catch (InterruptedException e) {
						return;
					}
					continue;
				}
			
			}
			
			
			// prepare receive packet
			int size = getBufferSize();
			
			byte[] buff = new byte[size];
			
			DatagramPacket packet = new DatagramPacket(buff, 0, size);
			
			// loop for receive data
			while(!stop) {
				
				try {
					
					socket.receive(packet);
					
					log.debug("data arrived. from=" + packet.getAddress() + " size=" + packet.getLength());
					
					dispatch(packet);
					
				} catch(SocketTimeoutException e) {
					
					continue;
					
				} catch (IOException e) {
					
					log.info("IOException happen, maybe config changed.");
					
					break;
					
				}
				
			}
		
		}
		
		if(socket != null) {
			try { socket.close(); } catch(Exception e){}
			socket = null;
		}
		
		
		log.debug("client is stopped.");
		
	}
	
	public abstract int getListenPort();
	
	public abstract int getBufferSize();
	
	public abstract void dispatch(DatagramPacket packet);

}
